/*
OpenMVL Matrix Vector Library
Copyright (c) 2009 FluidInteractive (R)
 
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising
from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
 
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
 
Written by: Nicola Candussi <nicola@fluidinteractive.com>
*/

//mat_layout.hpp

#ifndef MVL_MAT_LAYOUT_HPP
#define MVL_MAT_LAYOUT_HPP

#include "layout.hpp"

namespace mvl {

namespace layout {

//entry used in make_sparse
template<int Pos, typename Tag = _v_>
struct e 
{
    typedef Tag tag;
    enum { pos = Pos };
};

//mat sparse entries
template<typename I, typename J, typename Tag, typename Index>
struct mat_sparse_entry {
    typedef Tag                 tag;   //type of entry
    typedef I                   i;
    typedef J                   j;
    typedef Index               index; //index into the array of values
    typedef mat_sparse_entry    type;
};

//make a sparse layout 
template<int R, int C, typename Order, typename Entries>
struct make_sparse 
{
    //zeros and ones don't occupy any data
    template<typename Data, typename Pos, typename NumVals>
    struct state1 {
        typedef Data        data;
        typedef Pos         pos;
        typedef NumVals     num_vals;
        typedef state1      type;
    };

    template<typename Data, typename NumVals>
    struct state2 {
        typedef Data        data;
        typedef NumVals     num_vals;
        typedef state2      type;
    };

    //
    template<typename State, typename Se> struct compute_state;
    //case for entry == int
    template<typename State, int N> 
    struct compute_state<State, meta::int_<N> > 
    { 
        typedef typename meta::eval_if<
            typename meta::is_same<Order, row_major_tag>::type,
            mat_sparse_entry<typename State::pos, meta::int_<N>, _v_, typename State::num_vals>,
            mat_sparse_entry<meta::int_<N>, typename State::pos, _v_, typename State::num_vals>
        >::type new_entry;
               
        typedef state1<
            typename meta::push_back<
                typename State::data,
                new_entry
            >::type,
            typename State::pos,
            typename meta::next<typename State::num_vals>::type
        > type;
    };
    //case for entry == value
    template<typename State, int N> 
    struct compute_state<State, e<N, _v_> > 
    { 
        typedef typename meta::eval_if<
            typename meta::is_same<Order, row_major_tag>::type,
            mat_sparse_entry<typename State::pos, meta::int_<N>, _v_, typename State::num_vals>,
            mat_sparse_entry<meta::int_<N>, typename State::pos, _v_, typename State::num_vals>
        >::type new_entry;

        typedef state1<
            typename meta::push_back<
                typename State::data,
                new_entry
            >::type,
            typename State::pos,
            typename meta::next<typename State::num_vals>::type
        > type;
    };
    //case for entry == one
    template<typename State, int N> 
    struct compute_state<State, e<N, _1_> > 
    { 
        typedef typename meta::eval_if<
            typename meta::is_same<Order, row_major_tag>::type,
            mat_sparse_entry<typename State::pos, meta::int_<N>, _1_, meta::int_<-1> >,
            mat_sparse_entry<meta::int_<N>, typename State::pos, _1_, meta::int_<-1> >
        >::type new_entry;

        typedef state1<
            typename meta::push_back<
                typename State::data,
                new_entry
            >::type,
            typename State::pos,
            typename State::num_vals
        > type;
    };

    //case for entry == zero
    template<typename State, int N> 
    struct compute_state<State, e<N, _0_> > 
    { 
        typedef state1<
            typename State::data,
            typename State::pos,
            typename State::num_vals
        > type;
    };

    //
    struct converter {
        template<typename State, typename It> 
        struct apply : 
            compute_state<
                State,
                typename meta::deref<It>::type
            > { };
    };

    struct back_inserter {
        template<typename State, typename It>
        struct apply {
            typedef typename meta::iter_fold<
                typename meta::begin<typename meta::deref<It>::type>::type,
                typename meta::end<typename meta::deref<It>::type>::type,
                state1<
                    meta::vector<>,
                    typename meta::distance<
                        typename meta::begin<Entries>::type,
                        It
                    >::type,
                    typename State::num_vals
                >,
                converter
            >::type new_state;
            typedef state2<
                typename meta::push_back<
                    typename State::data,
                    typename new_state::data
                >::type,
                typename new_state::num_vals
            > type;
        };
    };

    //convert
    typedef typename meta::iter_fold<
        typename meta::begin<Entries>::type,
        typename meta::end<Entries>::type,
        state2<
            meta::vector<>,
            meta::int_<0>
        >,
        back_inserter
    >::type result;

    typedef sparse_layout<
        get1<meta::int_<C>, meta::int_<R>, Order>::value,
        get2<meta::int_<C>, meta::int_<R>, Order>::value,
        Order,
        typename result::data,
        result::num_vals::value
    > type;
};

} //namespace layout

} //namespace mvl


#endif


